home *** CD-ROM | disk | FTP | other *** search
/ Whiteline: delta / whiteline CD Series - delta.iso / fonts / ghost333 / ps / pdf_base.ps < prev    next >
Text File  |  1995-11-25  |  11KB  |  345 lines

  1. %    Copyright (C) 1994 Aladdin Enterprises.  All rights reserved.
  2.  
  3. % pdf_base.ps
  4. % Basic parser for PDF reader.
  5.  
  6. % This handles basic parsing of the file (including the trailer
  7. % and cross-reference table), as well as objects, object references,
  8. % and streams; it doesn't include any facilities for making marks on
  9. % the page.
  10.  
  11. /.setlanguagelevel where { pop 2 .setlanguagelevel } if
  12. .currentglobal true .setglobal
  13. /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
  14. pdfdict begin
  15.  
  16. % We rebind # and #dsc later if we're writing out PostScript.
  17. /#            % <arg1> ... <argN> <opname> <N> # -
  18.  { pop cvx exec
  19.  } bind def
  20. /#dsc            % mark <obj1> ... #dsc -
  21.  { cleartomark
  22.  } bind def
  23.  
  24. % Define the name interpretation dictionary for reading values.
  25. /valueopdict mark
  26.   (<<) cvn { mark } bind    % don't push an actual mark!
  27.   (>>) cvn /.dicttomark load
  28.   /[ { mark } bind        % ditto
  29.   /] /] load
  30.   /true true
  31.   /false false
  32.   /null null
  33.   /F dup cvx        % see Objects section below
  34.   /R dup cvx        % see Objects section below
  35.   /stream dup cvx    % see Streams section below
  36. .dicttomark readonly def
  37.  
  38. % ------ Utilities ------ %
  39.  
  40. % Define a scratch string.  The PDF language definition says that
  41. % no line in a PDF file can exceed 255 characters.
  42. /pdfstring 255 string def
  43.  
  44. % Read the previous line of a file.  If we aren't at a line boundary,
  45. % read the line containing the current position.
  46. /prevline        % - prevline <startpos> <substring>
  47.  { PDFfile fileposition pdfstring
  48.    1 index 257 sub 0 .max PDFfile exch setfileposition
  49.     { PDFfile fileposition
  50.       PDFfile 2 index readline pop
  51.         % Stack: initpos string startpos substring
  52.       PDFfile fileposition 4 index ge { exit } if
  53.       pop pop
  54.     }
  55.    loop 4 2 roll pop pop
  56.  } bind def
  57.  
  58. % Execute a file, interpreting its executable names in a given
  59. % dictionary.  The name procedures may do whatever they want
  60. % to the operand stack.
  61. /pdfrun            % <file> <opdict> pdfrun -
  62.  {    % Construct a procedure with the file and opdict bound into it.
  63.    1 index cvlit mark mark 4 2 roll
  64.     { token not { (%%EOF) cvn cvx } if
  65.       dup xcheck
  66.        { DEBUG { dup == flush } if
  67.      2 copy .knownget
  68.       { exch pop exch pop exec }
  69.       { .stderr dup (****************Unknown operator: ) writestring
  70.         exch .writecvs .stderr dup (\n) writestring flushfile
  71.         pop
  72.       }
  73.      ifelse
  74.        }
  75.        { exch pop DEBUG { dup ==only ( ) print flush } if
  76.        }
  77.       ifelse
  78.     }
  79.    aload pop .packtomark cvx
  80.    /loop cvx 2 packedarray cvx
  81.     { stopped /PDFsource } aload pop
  82.    PDFsource
  83.     { store { stop } if } aload pop .packtomark cvx
  84.    /PDFsource 3 -1 roll store exec
  85.  } bind def
  86.  
  87. % ------ File reading ------ %
  88.  
  89. % Read the cross-reference entry for an (unresolved) object.
  90. % The caller must save and restore the PDFfile position if desired.
  91. /readxrefentry        % <object#> readxrefentry <objpos>
  92.  { dup Objects exch get
  93.    PDFfile exch setfileposition
  94.    PDFfile token pop        % object position
  95.    PDFfile token pop        % generation #
  96.    PDFfile token pop        % n or f
  97.      /n ne { /readxrefentry cvx /syntaxerror signalerror } if
  98.    dup 255 gt
  99.     { Generations type /stringtype eq
  100.        {        % Convert Generations from a string to an array.
  101.      Generations length array dup
  102.      0 1 2 index length 1 sub
  103.       { Generations 1 index get put dup
  104.       }
  105.      for pop /Generations exch store
  106.        }
  107.       if
  108.     }
  109.    if
  110.         % Stack: obj# objpos gen#
  111.    Generations 4 -1 roll 3 -1 roll put
  112.  } bind def
  113.  
  114. % ================================ Objects ================================ %
  115.  
  116. % We represent an unresolved object reference by a procedure of the form
  117. % {obj# gen# resolveR}.  This is not a possible PDF object, because PDF has
  118. % no way to represent procedures.  Since PDF in fact has no way to represent
  119. % any PostScript object that doesn't evaluate to itself, we can 'force'
  120. % a possibly indirect object painlessly with 'exec'.
  121. % Note that since we represent streams by executable dictionaries
  122. % (see below), we need both an xcheck and a type check to determine
  123. % whether an object has been resolved.
  124. /unresolved?        % <object#> unresolved? <bool>
  125.  { Objects exch get dup xcheck exch type /integertype eq and
  126.  } bind def
  127. /oforce /exec load def
  128. /oget        % <array> <index> oget <object>
  129.         % <dict> <key> oget <object>
  130.  { 2 copy get dup xcheck
  131.     { exec dup 4 1 roll put }
  132.     { exch pop exch pop }
  133.    ifelse
  134.  } bind def
  135. % A null value in a dictionary is equivalent to an omitted key;
  136. % we must check for this specially.
  137. /knownoget
  138.  { 2 copy known
  139.     { oget dup null eq { pop false } { true } ifelse }
  140.     { pop pop false }
  141.    ifelse
  142.  } bind def
  143.  
  144. % PDF 1.1 defines a 'foreign file reference', but not its meaning.
  145. % Per the specification, we convert these to nulls.
  146. /F        % <file#> <object#> <generation#> F <object>
  147.  {        % Some PDF 1.1 files use F as a synonym for f!
  148.    count 3 lt { setfillcolor /f fs } { pop pop pop null } ifelse
  149.  } bind def
  150.  
  151. % We keep track of objects in a pair of arrays, Objects and Generations.
  152. % Generations[N] is the current generation number for object number N.
  153. % (As far as we can tell, this is needed only for error checking.)
  154. % If object N is loaded, Objects[N] is the actual object;
  155. % otherwise, Objects[N] is an executable integer giving the file offset
  156. % of the object's entry in the cross-reference table.
  157. /checkgeneration    % <object#> <generation#> checkgeneration <object#>
  158.  { Generations 2 index get 1 index ne
  159.     { (Wrong generation: ) print 1 index =only ( ) print dup =
  160.       /R cvx /rangecheck signalerror
  161.     }
  162.    if pop
  163.  } bind def
  164. /R        % <object#> <generation#> R <object>
  165.  { 1 index unresolved?
  166.     { /resolveR cvx 3 packedarray cvx }
  167.     { Objects 2 index get 3 1 roll checkgeneration pop }
  168.    ifelse
  169.  } bind def
  170.  
  171. % If we encounter an object definition while reading sequentially,
  172. % we just store it away and keep going.
  173. /objopdict mark
  174.   valueopdict { } forall
  175.   /endobj dup cvx
  176. .dicttomark readonly def
  177. /obj            % <object#> <generation#> obj <object>
  178.  { PDFfile objopdict pdfrun
  179.  } bind def
  180. /endobj            % <object#> <generation#> <object> endobj <object>
  181.  { 3 1 roll
  182.         % Read the xref entry if we haven't yet done so.
  183.         % This is only needed for generation # checking.
  184.    1 index unresolved?
  185.     { PDFfile fileposition
  186.       2 index readxrefentry pop
  187.       PDFfile exch setfileposition
  188.     } if
  189.    checkgeneration
  190.    Objects exch 2 index put
  191.  } bind def
  192.  
  193. % When resolving an object reference, we stop at the endobj.
  194. /resolveopdict mark
  195.   valueopdict { } forall
  196.   /endobj { endobj exit } bind
  197. .dicttomark readonly def
  198. /resolveR        % <object#> <generation#> resolveR <object>
  199.  { DEBUG { (Resolving: ) print 2 copy 2 array astore == } if
  200.    1 index unresolved?
  201.     { PDFfile fileposition 3 1 roll
  202.       1 index readxrefentry
  203.       3 1 roll checkgeneration
  204.         % Stack: savepos objpos obj#
  205.       exch PDFfile exch setfileposition
  206.       PDFfile token pop 2 copy ne
  207.        { (xref error!\n) print /resolveR cvx /rangecheck signalerror
  208.        }
  209.       if pop PDFfile token pop
  210.       PDFfile token pop /obj ne
  211.        { (xref error!\n) print /resolveR cvx /rangecheck signalerror
  212.        }
  213.       if
  214.       PDFfile resolveopdict pdfrun
  215.       exch PDFfile exch setfileposition
  216.     }
  217.     { pop Objects exch get
  218.     }
  219.    ifelse
  220.  } bind def      
  221.  
  222. %================================ Streams ================================ %
  223.  
  224. % We represent a stream by an executable dictionary that contains,
  225. % in addition to the contents of the original stream dictionary:
  226. %    /File - the file or string where the stream contents are stored;
  227. %    /FilePosition - iff File is a file, the position in the file
  228. %      where the contents start.
  229. % We do the real work of constructing the data stream only when the
  230. % contents are needed.
  231.  
  232. % Construct a stream.  The length is not reliable in the face of
  233. % different end-of-line conventions, but it's all we've got.
  234. % Since the stream keyword may be followed by 0, 1, or more blanks,
  235. % we have to back up in the file to find where the data actually starts.
  236. % The stream keyword may be followed by a blank line, which we also
  237. % must skip before reading any data.  (This is chancy if the data are
  238. % in binary form, but such files are questionable to begin with.)
  239. /streamskipeols
  240.  {  { PDFsource read not { /stream cvx /syntaxerror signalerror } if
  241.       dup 10 eq 1 index 13 eq or not { PDFsource exch unread exit } if pop
  242.     }
  243.    loop
  244.  } bind def
  245. /stream
  246.  { PDFsource PDFfile eq
  247.     { dup /File PDFfile put
  248.       prevline pop pop
  249.       streamskipeols
  250.       dup /FilePosition PDFfile fileposition put
  251.       PDFfile fileposition 1 index /Length oget add
  252.         PDFfile exch setfileposition
  253.     }
  254.     {    % We're already reading from a stream, which we can't reposition.
  255.     % Capture the sub-stream contents in a string.
  256.       streamskipeols
  257.       dup /Length oget string PDFsource exch readstring
  258.       not
  259.        { (Unexpected EOF in stream!\n) print
  260.      /stream cvx /rangecheck signalerror
  261.        }
  262.       if
  263.       1 index exch /File exch put
  264.     }
  265.    ifelse
  266.    PDFsource token pop
  267.      /endstream ne { /stream cvx /syntaxerror signalerror } if
  268.    cvx
  269.  } bind def
  270.  
  271. % Resolve a stream dictionary to a PostScript stream.
  272. % Streams with no filters require special handling:
  273. %    - If we are going to interpret their contents, we let endstream
  274. %      terminate the interpretation loop;
  275. %    - If we are just going to read data from them, we impose
  276. %      a SubFileDecode filter that reads just the requisite amount of data.
  277. % Note that, in general, resolving a stream repositions PDFfile.
  278. % Clients must save and restore the position of PDFfile themselves.
  279. /resolvestream        % <streamdict> <readdata?> resolvestream <stream>
  280.  { exch dup /FilePosition .knownget
  281.     { 1 index /File get exch setfileposition }
  282.    if
  283.         % Stack: readdata? dict
  284.    dup /DecodeParms .knownget not { null } if
  285.    1 index /Filter .knownget not { {} } if
  286.    dup type /nametype eq
  287.     { 1 array astore
  288.       1 index null ne { exch 1 array astore exch } if
  289.     }
  290.    if
  291.         % Stack: readdata? dict parms filternames
  292.    2 index /File get exch
  293.         % Stack: readdata? dict parms file/string filternames
  294.    dup length 0 eq
  295.     {        % All the PDF filters have EOD markers, but in this case
  296.         % there is no specified filter.
  297.       pop exch pop
  298.         % Stack: readdata? dict file/string
  299.       2 index
  300.        {    % We're going to read data; use a SubFileDecode filter.
  301.      1 index /Length oget () /SubFileDecode filter
  302.        }
  303.        { dup type /filetype ne
  304.       {    % Use a SubFileDecode filter to read from a string.
  305.         0 () SubFileDecode filter
  306.       }
  307.      if
  308.        }
  309.       ifelse
  310.     }
  311.     { 2 index null eq
  312.        { { filter }
  313.        }
  314.        {    % Stack: readdata? dict parms file/string filtername
  315.          { 2 index 0 get dup null eq { pop } { exch } ifelse filterpdf
  316.        exch dup length 1 sub 1 exch getinterval exch
  317.      }
  318.        }
  319.       ifelse forall exch pop
  320.     }
  321.    ifelse
  322.         % Stack: readdata? dict file
  323.    exch pop exch pop
  324.  } bind def
  325. /endstream { exit } def
  326.  
  327. % Construct a PDF filter.  These are the same as PostScript filters,
  328. % with one exception: LZWDecode filters with Predictor=2 must insert
  329. % a PixelDifferenceDecode filter in the pipeline.
  330. /filterpdf        % <source> <...params...> <name> filterpdf <stream>
  331.  { dup /LZWDecode eq 2 index type /dicttype eq and
  332.     { 1 index /Predictor .knownget not { 1 } if 1 sub
  333.        { { filter }
  334.      { 1 index 4 1 roll filter exch /PixelDifferenceDecode filter }
  335.        }
  336.       exch get exec
  337.     }
  338.     { filter
  339.     }
  340.    ifelse
  341.  } bind def
  342.  
  343. end            % pdfdict
  344. .setglobal
  345.